---
title: "Teacher Flow"
type: concept
created: 2026-04-18
updated: 2026-04-18
sources: ["raw/articles/11-user-signup-flows.md", "raw/articles/05-roster.md"]
tags: [user-flows, teacher, school-admin, registration, roster]
---

# Teacher Flow

The teacher flow covers both `school_admin` and `teacher` roles, as they share the same portal (`teacher.readingtester.com`). The school admin bootstraps the school; teachers manage classes and students.

## Flow A: School Admin Registers (School Bootstrap)

This is the first account in any school. Everything else depends on it.

**Entry point:** `account.readingtester.com/register` → "I'm setting up my school"

```mermaid
flowchart TD
    R["Register at account.readingtester.com\nname, email, password, school_name, country"]
    E["Email verification sent"]
    V["Click verify link\nusers.state → active\nschools.state → active"]
    W["School Setup Wizard\nteacher.readingtester.com/setup"]
    D1["Confirm school details\nyear groups, curriculum territory"]
    D2["Accept DPA\n⚠️ Required before any student data stored"]
    D3["Optional: invite teachers"]
    DB["Teacher Dashboard"]
    R --> E --> V --> W --> D1 --> D2 --> D3 --> DB
```

**What gets created:**
- `schools` row (name, country, state=`active`)
- `users` row (role=`school_admin`, school_id, state=`active`)
- `subscriptions` row (state=`trialing`, tier=`trial`, trial_ends_at=now()+14d)

**DPA is non-negotiable:** Data Processing Agreement must be accepted before any student data is stored. `schools.dpa_accepted_at` is the gate. The system must enforce this — no class creation without DPA.

## Flow B: Teacher Invited by School Admin

**Entry:** Teacher Portal → Settings → Team → Invite → enter email

**Invite email:** Contains link to `account.readingtester.com/invite?token=UUID` (expires 7 days)

**Teacher accepts:**
1. Lands on invite accept page
2. Fills in name + password
3. Account created instantly (no email verification needed — invite email serves as verification)
4. `memberships` row created (user_id, school_id, role=`teacher`)
5. Redirected to `teacher.readingtester.com/dashboard`

**Failure paths:**

| Condition | Error shown |
|---|---|
| Invite expired (>7 days) | "Invite expired. Ask admin to resend." |
| Invite already used | "Already used. Try signing in." |
| Email already registered | "Account exists. Sign in." |

## Flow C: Teacher Self-Registers (No Invite)

Same registration form as school_admin but with `role: "teacher"`. After email verification:

1. "Do you belong to a school?" → Yes / No
2. **Yes:** Search for school by name. If found → request to join (school_admin approves). If not found → create school (teacher becomes school_admin).
3. **No:** Teacher operates solo with individual subscription.

## Class Creation

After registration and DPA, teachers create classes:

**API:** `POST /api/v1/classes`
```json
{
  "class_name": "Year 3 Blue",
  "year_level": 3,
  "curriculum_territory": "England"
}
```

Each class links to one teacher and one school. A teacher can have multiple classes.

## Student Import

Two paths for adding students to a class:

**Single add:** Teacher Portal → Class → Add Student → modal. Returns `{username, pin}` shown once.

**Bulk CSV import:** Upload CSV with `name, year_level` columns. System generates username + PIN per row. Returns credential sheet for printing.

⚠️ **PIN shown once only.** After dismissing the modal, the plain PIN is not recoverable — teacher must Reset PIN if lost. PIN is stored only as a bcrypt hash in `students.pin_hash`.

## Login Cards

After student import, teacher can print login cards:
- One card per student
- Contains: student name, username, 4-digit PIN, QR code → `app.readingtester.com?user=username`
- School name, [[Pip]] logo, dashed border
- Generated as PDF via Puppeteer

**PIN retention:** Plain PIN available for 10 minutes after generation only (via `pin_reveal_tokens` table). After expiry, teacher must reset PIN to get a new one.

## Teacher Dashboard

> **⚠️ Non-Negotiable (Sig, 2026-04-18):** The dashboard answers four questions only. See [[concepts/user-flows/Teacher UI Rules]] for the full UI spec.

The teacher home screen answers:
1. Which class needs attention?
2. Which students have not started?
3. Who is struggling?
4. What should I assign next?

Entitlement logic, billing states, session policy, device trust, GDPR states, and tenant settings are **never shown** in the default teacher flow. They are hidden behind Advanced Settings or the admin panel.

**Design rule (Sig):** Light theme, NOT dark. Dense UI — everything visible on one screen. Big fonts, less clicks, no jargon.

## Attention List

The [[Learner Bot]] nightly run generates flags that appear in the teacher's attention list:

| Flag | Trigger |
|---|---|
| No placement test completed | First login without placement |
| 0 books read | Learner has no completed reading sessions |
| Low quiz average | avg quiz score <65% on 2+ consecutive quizzes |
| Inactive | No sessions in 4+ days |

Teachers can dismiss flags, add notes, and mark interventions complete.

## Related Pages

- [[concepts/user-flows/index|User Flows]] — all roles overview
- [[concepts/user-flows/Student Flow]] — what happens after teacher creates the student
- [[entities/Teacher Portal]] — service entity page
- [[entities/Account Center]] — SSO hub where registration/login lives
